package cn.lg.soar.mvc.util;

import cn.lg.soar.common.data.msg.I18n;
import cn.lg.soar.common.exceptions.AuthenticationException;
import cn.lg.soar.common.exceptions.ParameterException;
import cn.lg.soar.common.exceptions.PermissionException;
import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.util.FileTypeUtils;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.jackson.JacksonUtil;
import com.alibaba.excel.support.ExcelTypeEnum;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * 响应工具
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class ResponseUtils {

    protected static final Logger LOGGER = LoggerFactory.getLogger(ResponseUtils.class);

    private final static String CONTENT_TYPE_HTML = "text/html;charset=UTF-8";
    private final static String LOGIN_JSON = JacksonUtil.toJson(HttpResult.fail(new AuthenticationException()));
    private final static String PERMISSION_JSON = JacksonUtil.toJson(HttpResult.fail(new PermissionException()));
    private final static String ERROR_JSON = JacksonUtil.toJson(HttpResult.fail());

    public static void responseBody(HttpServletResponse response, String text) {
        response.setContentType(ResponseUtils.CONTENT_TYPE_HTML);
        try (PrintWriter out = response.getWriter()){
            out.write(text);
            out.flush();
        } catch (IOException e) {
            LOGGER.error("写响应内容失败", e);
        }
    }

    public static void responseBody(HttpServletResponse response, Object data) {
        ResponseUtils.responseBody(response, JacksonUtil.toJson(data));
    }

    public static void responseBody(String text) {
        responseBody(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse(), text);
    }

    public static void responseBody(Object data) {
        responseBody(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse(), JacksonUtil.toJson(data));
    }

    public static void E400(HttpServletResponse response, I18n msg, Object... args){
        response.setStatus(400);
        ResponseUtils.responseBody(response, HttpResult.fail(new ParameterException(msg, args)));
    }

    public static void E401(HttpServletResponse response){
        response.setStatus(401);
        ResponseUtils.responseBody(response, ResponseUtils.LOGIN_JSON);
    }

    public static void E403(HttpServletResponse response){
        response.setStatus(403);
        ResponseUtils.responseBody(response, ResponseUtils.PERMISSION_JSON);
    }

    public static void E412(HttpServletResponse response, String msg){
        response.setStatus(412);
        ResponseUtils.responseBody(response, msg);
    }

    public static void E500(HttpServletResponse response){
        response.setStatus(500);
        ResponseUtils.responseBody(response, ResponseUtils.ERROR_JSON);
    }

    public static void setResponseExcel(String fileName, HttpServletResponse response) {
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.setHeader(
                    "Content-disposition",
                    "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ExcelTypeEnum.XLSX.getValue()
            );
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**.
     *
     *
     * @param fileName
     * @param response
     */
    public static void setResponseExcel(int status, HttpServletResponse response) {
        response.setStatus(status);
//        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }

    /**
     * 跨域配置
     * @param response
     * @param origin
     * @param methods
     */
    public static void corsConfig(HttpServletResponse response, String origin, String methods, String headers, String age) {
        response.addHeader("Access-Control-Allow-Origin", origin);
        response.addHeader("Access-Control-Allow-Methods", methods);
        response.addHeader("Access-Control-Allow-Headers", headers);
        response.addHeader("Access-Control-Allow-Max-Age", age);
    }
    public static void corsConfig(HttpServletResponse response, String origin, String methods, String headers){
        corsConfig(response, origin, methods, headers, "3600");
    }
    public static void corsConfig(HttpServletResponse response, String origin, String methods){
        corsConfig(response, origin, methods, "*", "3600");
    }
    public static void corsConfig(HttpServletResponse response, String origin){
        corsConfig(response, origin, "*", "*", "3600");
    }
    public static void corsConfig(HttpServletResponse response){
        corsConfig(response, "*", "*", "*", "3600");
    }

    public static HttpServletResponse getResponse() {
        return ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getResponse();
    }

    /**
     * 文件流响应
     * @param inputStream 文件
     * @param filename 文件名称
     */
    public static void responseFile(Path path, String filename) {
        File file = path.toFile();
        if(!file.exists()) {
            return;
        }
        try (InputStream inputStream = Files.newInputStream(path)) {
            responseFile(file.length(), inputStream, filename);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件流响应
     * @param contentLength 内容长度
     * @param inputStream 输入流
     */
    public static void responseFile(long contentLength, InputStream inputStream) {
        responseFile(contentLength, inputStream, null);
    }

    /**
     * 文件流响应
     * @param contentLength 内容长度
     * @param inputStream 输入流
     * @param filename 文件名称
     */
    public static void responseFile(long contentLength, InputStream inputStream, String filename) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpServletResponse response = requestAttributes.getResponse();
        try (inputStream; OutputStream outputStream = response.getOutputStream()) {
            try {
                response.setCharacterEncoding("UTF-8");
                if (DataUtil.isValuable(filename)) {
                    response.setContentType(FileTypeUtils.getContentType(filename) + "; charset=UTF-8");
                    // 根据不同的客户端进行不同的编码
                    String agent = request.getHeader("User-Agent");
                    if (agent.contains("Firefox")) {
                        filename = "=?utf-8?B?" + DataUtil.byteToBase64(filename.getBytes(StandardCharsets.UTF_8)) + "?=";
                    } else {
                        filename = URLEncoder.encode(filename, "utf-8");
                    }
                    response.setHeader("Content-Disposition", "attachment;fileName=\"" + filename + "\"");
                }
                // 解析Range头
                String rangeHeader = request.getHeader("Range");
                if (contentLength > 0 && StringUtils.hasText(rangeHeader)) {
                    if (rangeHeader.startsWith("bytes=")) {
                        // 范围响应
                        long fileLength = contentLength;
                        long start;
                        long end = fileLength - 1;
                        // 解析范围
                        String[] ranges = rangeHeader.substring(6).split("-");
                        try {
                            start = Long.parseLong(ranges[0]);
                            if (ranges.length > 1) {
                                end = Long.parseLong(ranges[1]);
                            }
                        } catch (NumberFormatException e) {
                            throw new RuntimeException(e);
                        }
                        // 计算内容长度
                        contentLength = end - start + 1;
                        // 设置响应头
                        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                        response.setContentLengthLong(contentLength);
                        response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
                        response.setHeader("Accept-Ranges", "bytes");
                        // 跳过起始位置之前的字节
                        inputStream.skip(start);
                        byte[] buffer = new byte[4096];
                        int read;
                        while (contentLength > 0 && (read = inputStream.read(buffer, 0, (int) Math.min(buffer.length, contentLength))) != -1) {
                            outputStream.write(buffer, 0, read);
                            contentLength -= read;
                        }
                        outputStream.flush();
                        return;
                    }
                }
                // 正常输出
                response.setStatus(HttpServletResponse.SC_OK);
                if (contentLength > 0) {
                    response.setContentLengthLong(contentLength);
                }
                byte[] buffer = new byte[4096];
                int read;
                while ((read = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, read);
                }
                outputStream.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } catch (IOException e) {
            LOGGER.error("关闭流异常", e);
        }
        // 关闭输入流，输出流语法糖处理了
    }

}
